package com.youlu.campus.service.english;

import com.alibaba.fastjson.JSON;
import com.youlu.campus.base.exception.BusinessException;
import com.youlu.campus.entity.UserPO;
import com.youlu.campus.entity.WechatConfigInfo;
import com.youlu.campus.entity.WechatMerchantConfig;
import com.youlu.campus.entity.english.*;
import com.youlu.campus.entity.english.vo.request.CompetitionPlaceOrderCommand;
import com.youlu.campus.entity.english.vo.request.UserCompetitionFeeQuery;
import com.youlu.campus.service.base.BaseService;
import com.youlu.campus.service.wechat.WechatConfigInfoService;
import com.youlu.campus.service.wechat.WechatMerchantConfigService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Slf4j
@Service
public class UserCompetitionFeeService extends BaseService {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private StringRedisTemplate redisTemplate;
    @Lazy
    @Autowired
    private ActivityGroupService activityGroupService;
    @Lazy
    @Autowired
    private UserCompetitionService userCompetitionService;

    @Autowired
    private WechatConfigInfoService wechatConfigInfoService;
    @Autowired
    private WechatMerchantConfigService wechatMerchantConfigService;

    final static String CACHE_ORDER = "campus:english:order:%s";


    final static String USER_QA_ORDER = "campus:english:qa:%s:%s";

    @Async
    public void updateUserInfo(UserCompetition userCompetition) {
        Query query = new Query();
        query.addCriteria(Criteria.where("userId").is(userCompetition.getUserId()));
        query.addCriteria(Criteria.where("status").is(1));
        Update update = new Update();
        update.set("name", userCompetition.getName());
        update.set("universityId", userCompetition.getUniversityId());
        update.set("universityName", userCompetition.getUniversityName());
        update.set("updatedTime", new Date());
        this.mongoTemplate.updateMulti(query, update, UserCompetitionFee.class);
    }


    public String feeByUserId(String userId) {
        Query query = new Query();
        query.addCriteria(Criteria.where("userId").is(userId));
        query.addCriteria(Criteria.where("status").is(1));
        List<UserCompetitionFee> list = this.mongoTemplate.find(query, UserCompetitionFee.class);
        if (CollectionUtils.isEmpty(list)) {
            return "";
        }
        return list.stream().peek(user -> {
            String feeTypeCode = user.getFeeTypeCode();
            if (ActivityQaFeeDetail.BAO_MING_QA_FEE.equals(feeTypeCode)) {
                user.setFeeTypeCode(user.getActivityGroupName() + "【" + "名+题库" + "】");
            }
            if (ActivityQaFeeDetail.BAO_MING_FEE.equals(feeTypeCode)) {
                user.setFeeTypeCode(user.getActivityGroupName() + "【" + "纯报名费" + "】");
            }
            if (ActivityQaFeeDetail.QA_FEE.equals(feeTypeCode)) {
                user.setFeeTypeCode(user.getActivityGroupName() + "【" + "题库费" + "】");
            }
        }).map(UserCompetitionFee::getFeeTypeCode).collect(Collectors.joining());
    }


    public Page<UserCompetitionFee> list(UserCompetitionFeeQuery userCompetitionFeeQuery) {

        Query query = new Query();
        if (StringUtils.isNotBlank(userCompetitionFeeQuery.getOrderNo())) {
            query.addCriteria(Criteria.where("orderNo").is(userCompetitionFeeQuery.getOrderNo()));
        }
        if (StringUtils.isNotBlank(userCompetitionFeeQuery.getMobile())) {
            query.addCriteria(Criteria.where("mobile").is(userCompetitionFeeQuery.getMobile()));
        }
        if (StringUtils.isNotBlank(userCompetitionFeeQuery.getName())) {
            Pattern pattern = Pattern.compile("^.*" + userCompetitionFeeQuery.getName() + ".*$", Pattern.CASE_INSENSITIVE);
            query.addCriteria(Criteria.where("name").regex(pattern));
        }
        if (Objects.nonNull(userCompetitionFeeQuery.getStatus())) {
            query.addCriteria(Criteria.where("status").is(userCompetitionFeeQuery.getStatus()));
        }
        PageRequest pageRequest = PageRequest.of(userCompetitionFeeQuery.getPageNo() - 1, userCompetitionFeeQuery.getPageSize());
        long count = this.mongoTemplate.count(query, UserCompetitionFee.class);
        if (count == 0) {
            PageableExecutionUtils.getPage(new ArrayList<>(), pageRequest, () -> count);
        }
//        query.with(new Sort(Sort.Direction.DESC, "createdTime"));
        Sort sort=Sort.by(Sort.Direction.DESC, "createdTime");
        query.with(sort);
        List<UserCompetitionFee> list = this.mongoTemplate.find(query.with(pageRequest), UserCompetitionFee.class);
        return PageableExecutionUtils.getPage(list, pageRequest, () -> 5000);
    }

    public void updateByQaType(String userId, String activityGroupId, String feeTypeCode, String qaInfoId, String qaCode) {
        log.info("【英语大赛】更新用户答题升级 userId - > {} | activityGroupId - >{} | feeTypeCode-> {}| qaInfoId - > {} | qaCode " +
                "-> {}", userId, activityGroupId, feeTypeCode, qaInfoId, qaCode);
        Query query = new Query();
        query.addCriteria(Criteria.where("userId").is(userId));
        query.addCriteria(Criteria.where("activityGroupId").is(activityGroupId));
        query.addCriteria(Criteria.where("status").is(1));
        query.addCriteria(Criteria.where("feeTypeCode").is(feeTypeCode));
        UserCompetitionFee userCompetitionFee = this.mongoTemplate.findOne(query, UserCompetitionFee.class);
        if (ActivityQaFeeDetail.BAO_MING_QA_FEE.equals(feeTypeCode)) {
            if (Objects.isNull(userCompetitionFee)) {
                List<UserCompetitionFee> list = this.findByUserIdAndActivityGroupId(userId, activityGroupId);
                if (CollectionUtils.isEmpty(list)) {
                    throw new BusinessException("对不起您未报名,不能参与答题");
                }
                Optional<UserCompetitionFee> userCompetitionFeeOptional = list.stream().filter(userFee -> ActivityQaFeeDetail.BAO_MING_FEE.equals(userFee.getFeeTypeCode())).findFirst();
                if (userCompetitionFeeOptional.isPresent()) {
                    userCompetitionFee = userCompetitionFeeOptional.get();
                }
            }
        }
        List<ActivityGroupQaType> buyQaTypeList = null;
        switch (qaCode) {
            case ActivityGroupQaType.QA_CHU_SAI:
                buyQaTypeList = userCompetitionFee.getBuyQaTypeList().stream().map(buyQaType -> {
                    if (ActivityGroupQaType.QA_FU_SAI.equals(buyQaType.getCode())) {
                        buyQaType.setLock(Boolean.TRUE);
                        return buyQaType;
                    }
                    return buyQaType;
                }).collect(Collectors.toList());

                break;
            case ActivityGroupQaType.QA_FU_SAI:
                buyQaTypeList = userCompetitionFee.getBuyQaTypeList().stream().map(buyQaType -> {
                    if (ActivityGroupQaType.QA_JUE_SAI.equals(buyQaType.getCode())) {
                        buyQaType.setLock(Boolean.TRUE);
                        return buyQaType;
                    }
                    return buyQaType;
                }).collect(Collectors.toList());
                break;
            case ActivityGroupQaType.QA_JUE_SAI:
                buyQaTypeList = userCompetitionFee.getBuyQaTypeList();
                break;
            default:
        }
        userCompetitionFee.setBuyQaTypeList(buyQaTypeList);
        this.mongoTemplate.save(userCompetitionFee);
        setUserQaOrder(userCompetitionFee);

    }


    public UserCompetitionFee verificationUserFee(String userId, String activityGroupId, String feeTypeCode) {
        log.info("验证用户是否购买费用 userId ->{} |activityGroupId->{} |feeTypeCode->{} ", userId, activityGroupId, feeTypeCode);
        Query query = new Query();
        query.addCriteria(Criteria.where("userId").is(userId));
        query.addCriteria(Criteria.where("activityGroupId").is(activityGroupId));
        query.addCriteria(Criteria.where("feeTypeCode").is(feeTypeCode));
        query.addCriteria(Criteria.where("status").is(1));
        return this.mongoTemplate.findOne(query, UserCompetitionFee.class);
    }

    public UserCompetitionFee findActivityGroupIdAndQaInfoId(String userId, String activityGroupId, String qaInfoId) {
        UserCompetitionFee userCompetitionFee = this.findActivityGroupId(userId, activityGroupId);
        if (Objects.isNull(userCompetitionFee)) {
            throw new BusinessException("对不起,你未购买当前服务");
        }
        long count = userCompetitionFee.getBuyQaTypeList().stream().filter(qaInfoType -> qaInfoType.getQaInfoId().equals(qaInfoId) && Boolean.TRUE.equals(qaInfoType.getLock())).count();
        if (count == 0) {
            throw new BusinessException("对不起,你所答题未进未解锁不能进行答题");
        }
        return userCompetitionFee;
    }


    public UserCompetitionFee findActivityGroupId(String userId, String activityGroupId) {
        List<UserCompetitionFee> userCompetitionFeeList = findByUserIdAndActivityGroupId(userId, activityGroupId);
        if (CollectionUtils.isEmpty(userCompetitionFeeList)) {
            return null;
        }
        //购买报名费+ 题库
        Optional<UserCompetitionFee> baoMingQaOptional = userCompetitionFeeList.stream().filter(userCompetitionFee -> ActivityQaFeeDetail.BAO_MING_QA_FEE.equals(userCompetitionFee.getFeeTypeCode())).findAny();
        if (baoMingQaOptional.isPresent()) {
            return baoMingQaOptional.get();
        }
        //购买题库
        Optional<UserCompetitionFee> qaOptional = userCompetitionFeeList.stream().filter(userCompetitionFee -> ActivityQaFeeDetail.QA_FEE.equals(userCompetitionFee.getFeeTypeCode())).findAny();
        //购买报名费
        Optional<UserCompetitionFee> baoMingOptional = userCompetitionFeeList.stream().filter(userCompetitionFee -> ActivityQaFeeDetail.BAO_MING_FEE.equals(userCompetitionFee.getFeeTypeCode())).findAny();
        //既购买了报名费也购买了题库
        if (baoMingOptional.isPresent() && qaOptional.isPresent()) {
            UserCompetitionFee userCompetitionFee = baoMingOptional.get();
            userCompetitionFee.setFeeTypeCode(ActivityQaFeeDetail.BAO_MING_QA_FEE);
            return userCompetitionFee;
        }
        if (baoMingOptional.isPresent()) {
            return baoMingOptional.get();
        }
        if (qaOptional.isPresent()) {
            return qaOptional.get();
        }
        return null;
    }

    private List<UserCompetitionFee> findByUserIdAndActivityGroupId(String userId, String activityGroupId) {
        Query query = new Query();
        query.addCriteria(Criteria.where("userId").is(userId));
        query.addCriteria(Criteria.where("activityGroupId").is(activityGroupId));
        query.addCriteria(Criteria.where("status").is(1));
        return this.mongoTemplate.find(query, UserCompetitionFee.class);
    }

    public List<UserCompetitionFee> userBuyfindActivityGroupId(String userId, String activityGroupId) {
        List<UserCompetitionFee> userCompetitionFeeList = findByUserIdAndActivityGroupId(userId, activityGroupId);
        if (CollectionUtils.isEmpty(userCompetitionFeeList)) {
            return null;
        }
        return userCompetitionFeeList;
    }

    public UserCompetitionFee placeOrder(CompetitionPlaceOrderCommand command) {
        UserPO currentUser = command.getCurrentUser();
        String activityGroupId = command.getActivityGroupId();
        String feeTypeCode = command.getFeeTypeCode();
        String userId = currentUser.getId();
        UserCompetition userCompetition = userCompetitionService.findByUserId(currentUser.getId());
        if (Objects.isNull(userCompetition)) {
            throw new BusinessException("未找到您的报名信息,请报名填写信息");
        }
        ActivityGroup activityGroup = activityGroupService.findById(activityGroupId);
        List<ActivityQaFeeDetail> activityFeeDetails = activityGroup.getActivityFeeDetails();
        Optional<ActivityQaFeeDetail> feeDetailsOptional =
                activityFeeDetails.stream().filter(fee -> fee.getFeeTypeCode().equals(feeTypeCode)).findFirst();
        if (!feeDetailsOptional.isPresent()) {
            throw new BusinessException("购买费用类别不存在");
        }
        WechatConfigInfo wechatConfigInfo = wechatConfigInfoService.find(currentUser.getAppId());
        if (Objects.isNull(wechatConfigInfo) || Objects.isNull(wechatConfigInfo.getMerchantIds())||CollectionUtils.isEmpty(wechatConfigInfo.getMerchantIds())) {
            log.error("微信公众号配置错误:{}", currentUser.getAppId());
            throw new BusinessException("当前支付人数多,稍后重试");
        }
        WechatMerchantConfig merchantConfig = wechatMerchantConfigService.findByMerchantIdAndDecrypt(wechatConfigInfo.getMerchantId());
        if (Objects.isNull(merchantConfig)) {
            log.error("当前支付人数多,稍后重试,商户号配置错误");
            throw new BusinessException("微信支付服务器异常,稍后重试");
        }
        ActivityQaFeeDetail activityQaFeeDetail = feeDetailsOptional.get();
        String orderNo = super.generateEOrderNo("E");
        UserCompetitionFee userCompetitionFee = new UserCompetitionFee();
        userCompetitionFee.setActivityGroupId(activityGroupId);
        userCompetitionFee.setActivityId(activityGroup.getActivityId());
        userCompetitionFee.setActivityName(activityGroup.getActivityName());
        userCompetitionFee.setActivityGroupName(activityGroup.getGroupName());
        userCompetitionFee.setFeeTypeCode(feeTypeCode);
        userCompetitionFee.setFeeName(activityQaFeeDetail.getFeeName());
        userCompetitionFee.setPrice(activityQaFeeDetail.getPrice());
        userCompetitionFee.setUserId(userId);
        userCompetitionFee.setOrderNo(orderNo);
        userCompetitionFee.setName(userCompetition.getName());
        userCompetitionFee.setMobile(userCompetition.getMobile());
        userCompetitionFee.setUniversityId(userCompetition.getUniversityId());
        userCompetitionFee.setUniversityName(userCompetition.getUniversityName());
        userCompetitionFee.setBuyQaTypeList(activityGroup.getQaTypeList());
        userCompetitionFee.setAppId(currentUser.getAppId());
        userCompetitionFee.setMerchantId(merchantConfig.getMerchantId());
        userCompetitionFee.setMerchantName(merchantConfig.getMerchantName());
        this.mongoTemplate.save(userCompetitionFee);
        this.redisTemplate.opsForValue().set(String.format(CACHE_ORDER, orderNo),
                JSON.toJSONString(userCompetitionFee), 10L, TimeUnit.MINUTES);
        return userCompetitionFee;
    }

    public UserCompetitionFee findOrderNo(String orderNo) {
        String cacheOrderNo = String.format(CACHE_ORDER, orderNo);
        String cacheOrderInfo = this.redisTemplate.opsForValue().get(cacheOrderNo);
        if (StringUtils.isNotBlank(cacheOrderInfo)) {
            return JSON.parseObject(cacheOrderInfo, UserCompetitionFee.class);
        }
        Query query = new Query();
        query.addCriteria(Criteria.where("orderNo").is(orderNo));
        return this.mongoTemplate.findOne(query, UserCompetitionFee.class);
    }

    public void setUserQaOrder(UserCompetitionFee order) {
        String userId = order.getUserId();
        String activityGroupId = order.getActivityGroupId();
        String qaUser = String.format(USER_QA_ORDER, userId, activityGroupId);
        this.redisTemplate.delete(qaUser);
        this.redisTemplate.opsForValue().set(qaUser, JSON.toJSONString(order), 1L, TimeUnit.HOURS);
        String orderNoCache = String.format(CACHE_ORDER, order.getOrderNo());
        this.redisTemplate.delete(orderNoCache);
        this.redisTemplate.opsForValue().set(orderNoCache, JSON.toJSONString(order), 1L, TimeUnit.HOURS);
    }

}
